#region (c) 2014 THN Solutions LLC. - All rights reserved
/*
THN DATA ABSTRACTION LAYER version 1

Copyright (c) 2014, THN Solutions LLC. ( www.ThnSolutions.com )
Author: Nguyễn, M. Hải                 ( www.Minh-Hai.com     )
All rights reserved.

This library is dual-licensed.
  + For commercial software, please obtain a commercial license from THN Solutions LLC.

  + For free software, this library is licensed under GPL version 3. A summary of GPLv3 is
    listed below. You should also find a copy of GPLv3 as file License_GPLv3.txt included
	with the source-code files.

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License version 3 as published by
    the Free Software Foundation  of the License.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
#endregion

#region Using directives
using System;
using System.IO;
using System.Collections;
using Thn.Application;
using Thn.Serialization;
#endregion

namespace Thn.Data.Database.Memory
{
    /// <summary>
    /// An in-memory database that store data in xml files
    /// </summary>
    public class XmlDatabase : InMemoryDatabase
    {
        #region File handling

        #region Serializer
        private ISerializer mSerializer = null;
        /// <summary>
        /// Gets/Sets the serializer to be used in saving and loading file
        /// </summary>
        public ISerializer Serializer
        {
            get { return mSerializer; }
            set { mSerializer = value; }
        }
        #endregion

        #region File Extension
        private string mFileExtension = ".xml";
        /// <summary>
        /// Gets/Sets FileExtension
        /// </summary>
        public string FileExtension
        {
            get { return mFileExtension; }
            set
            {
                if (mFileExtension != value)
                {
                    mFileExtension = value;
                    if (!mFileExtension.StartsWith(".")) mFileExtension = "." + mFileExtension;
                }
            }
        }
        #endregion

        #region Path
        private string mPath = string.Empty;
        /// <summary>
        /// Gets/Sets name of the folder which contains data files
        /// </summary>
        public string Path
        {
            get { return mPath; }
            set
            {
                if (mPath != value)
                {
                    mPath = value;
                    if (!mPath.EndsWith(@"\")) mPath = mPath + @"\";
                }
            }
        }
        #endregion

        #region Get Filename
        /// <summary>
        /// Build the absolute filename for a data type (Path + Type's name + File extension)
        /// </summary>
        string GetFilename(Type dataType)
        {
            return mPath + dataType.Name + mFileExtension;            
        }
        #endregion

        #region Save to file
        /// <summary>
        /// Serialize all data objects to files
        /// </summary>
        protected void Save()
        {
            foreach (Type t in RegisteredTypes)
            {
                Save(t);
            }
        }

        /// <summary>
        /// Serialize all data of a specific data type and save to file
        /// </summary>
        /// <param name="dataType">Type of data object</param>
        protected void Save(Type dataType)
        {
            //if has data, save list
            //if not, save empty list

            IList data = GetObjects(dataType);

            if (data == null) data = new ArrayList();

            //ensure path is valid
            string filename = GetFilename(dataType);
            if (!Directory.Exists(Path)) Directory.CreateDirectory(Path);
            
            //serialize to file
            if (data.Count > 0)
                //((CompactXmlSerializer)mSerializer).SaveToXmlFile(filename, data);
                mSerializer.Save(filename, data);
            else
            {
                if (File.Exists(filename)) File.Delete(filename);
            }
        }
        #endregion

        #region Load from file
        /// <summary>
        /// Read and deserialize all data of a specific data type from file
        /// </summary>
        /// <param name="dataType">Type of data object</param>
        protected IList Load(Type dataType)
        {
            IList result = null;

            string filename = GetFilename(dataType);
            if (File.Exists(filename))
            {
                //object tmp = ((CompactXmlSerializer)mSerializer).LoadFromXmlFile(filename);
                object tmp = mSerializer.Load(filename);
                if (tmp == null) throw new Thn.Data.Exceptions.DataExceptionBase("Could not load from file " + filename);
                else if (tmp is IList)
                {
                    result = (IList)tmp;
                }
                else throw new Thn.Data.Exceptions.DataExceptionBase("Invalid data file format for type [" + dataType.Name + "]. Loaded type: " +tmp.GetType().Name);
            }
            else Log.Warning("Could not locate data file for type [" + dataType.Name + "]");

            return result;
        }
        #endregion

        #endregion

        #region I/O
        #region Initialize
        /// <summary>
        /// Prepare connection to database
        /// </summary>
        /// <param name="parameters">connection string</param>
        public override void Initialize(string parameters)
        {
            Path = parameters;
        }
        #endregion

        #region Refresh
        ///// <summary>
        ///// Refresh/load data, if neccessary
        ///// </summary>
        //public override void Refresh()
        //{
        //}
        #endregion

        #region Flush
        /// <summary>
        /// Persist changes to database, if still buffered
        /// </summary>
        public override void Flush()
        {
            Save();
        }
        #endregion

        #region Close
        /// <summary>
        /// Close connection to the underlying database, if neccessary
        /// </summary>
        public override void Close()
        {
            Flush();
        }
        #endregion

        #endregion

        #region Structuring
        #region Create structure for 1 object type
        /// <summary>
        /// Generate database structure specific to a single object type
        /// </summary>
        /// <param name="objectType">type of data object to generate</param>
        /// <param name="keepOldData">try to retain the existing data</param>
        public override void CreateStructure(Type objectType, bool keepOldData)
        {
            Save(objectType);
        }
        #endregion
        #endregion

        #region Load Objects
        /// <summary>
        /// Get all objects of a specific type
        /// </summary>
        /// <param name="objectType">type of data object</param>
        protected override IList OnGetObjects(Type objectType)
        {
            return Load(objectType);
        }
        #endregion

        #region Constructors
        /// <summary>
        /// Default constructor
        /// </summary>
        public XmlDatabase()
        {
            XmlSerializer serializer = new XmlSerializer();
            serializer.RequiredAttributes = new Attribute[] { new SavableAttribute() };
            mSerializer = serializer;
        }
        #endregion
    }
}
